using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
using System.Diagnostics;
using System.Collections.Generic;

namespace MediaWikiEngine.Domain
{
	/// <summary>
	/// Summary description for WikiPage.
	/// </summary>
	public class WikiPage
	{
		string _rawData = string.Empty;
		string _wikiMarkup = string.Empty;
        string _namespace = string.Empty;
        string _title = string.Empty;
        bool _exists = false;
        //XmlDocument _pageDoc = null;
        bool? _watch = null;
        List<string> _categories = new List<string>();

        /// <summary>
        /// Gets or sets a flag indicating the watch status of the page, with true indicating that 
        /// the user is watching the page, and false indicating otherwise. Returns null when the watch
        /// status is not known or has not been set for a new page.
        /// </summary>
        public bool? Watch { get { return _watch; } set { _watch = value; } }

        /// <summary>
        /// Gets or sets the wiki markup of a page.
        /// </summary>
		public string WikiMarkup
		{
			get { return _wikiMarkup.Replace("\n","\r\n"); }
			set
			{
				_wikiMarkup = value.Replace("\r\n","\n");
			}
		}

        /// <summary>
        /// Gets a list of the titles of categories used on a page.
        /// </summary>
        public List<string> Categories
        {
            get { return _categories; }
        }

        /// <summary>
        /// Gets or sets a flag indicating that this page exists on the wiki.
        /// </summary>
        public bool Exists
        {
            get { return _exists; }
            set { _exists = value; }
        }

        /// <summary>
        /// Gets or sets the raw XHTML data for the page.  The page will be parsed when this value is set.
        /// </summary>
		public string RawData
		{
			get { return _rawData; }
			set
			{
				_rawData = value;

				Parse();   
			}
		}

        /// <summary>
        /// Gets or sets the title of this page.
        /// </summary>
        public string Title
        {
            get { return _title; }
            set { _title = value; }
        }

        /// <summary>
        /// Gets or sets the MediaWiki namespace of this page.
        /// </summary>
        /// <remarks>Once localization support is finished, we may need to distinguish between the local 
        /// and the default (English) namespace.</remarks>
        public string Namespace
        {
            get { return _namespace; }
            set { _namespace = value; }
        }

        /// <summary>
        /// Creates a new page object from raw XHTML data.
        /// </summary>
        /// <param name="rawData">The XHTML data for the page.  The data will be parsed.</param>
		public WikiPage(string rawData)
		{
			RawData = rawData;
		}

        /// <summary>
        /// Creates a blank wiki page object.
        /// </summary>
		public WikiPage()
		{

		}

        /// <summary>
        /// Gets the value of an input XHTML field on the page based on the <see cref="RawData"/>.
        /// </summary>
        /// <param name="fieldName">The name of the XHTML input field.</param>
		public string GetInputFieldValue(string fieldName)
		{
			string pattern = @"<input (?:.*?name=""" + fieldName + @""".*?value=""(?<value>.*?)"".*?|.*?value=""(?<value>.*?)"".*?name=""" + fieldName + @""".*?)>";
            
            Match m = Regex.Match(_rawData, pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);

			if (false == m.Success)
				return string.Empty;

			return m.Groups["value"].Value;
		}

        /// <summary>
        /// Should be called prior to writing the content of the page to the wiki in order to ensure categories 
        /// are up-to-date.
        /// </summary>
        public void PrepareForSave()
        {
            ReplaceCategories();
        }

		private void Parse()
		{
			string StartPattern = @"<textarea[^\>]*?>";
			string EndPattern = @"</textarea";

			Match m = Regex.Match(_rawData, StartPattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);
            
			if (false == m.Success)
			{
				throw new ApplicationException("no textarea");
			}

			int startIndex = m.Index + m.Length;

			m = Regex.Match(_rawData, EndPattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);

			if (false == m.Success)
			{
				throw new ApplicationException("no end textarea");
			}

			int endIndex = m.Index;
			
			_wikiMarkup = HttpUtility.HtmlDecode(_rawData.Substring(startIndex, endIndex - startIndex));

            if (null == _wikiMarkup || _wikiMarkup.Length == 0 || _wikiMarkup == "\n")
            {
                //maybe there's no page yet?

                try
                {
                    if (false == _rawData.Contains("<li id=\"ca-history\">"))
                        _exists = false;
                    
                    //the following algorithm is more sure-fire, but loading xml data takes a lot longer
                    /*

                    _pageDoc = new XmlDocument();
                    _pageDoc.LoadXml(_rawData);

                    //we're loading xhtml, so need to manage namespaces
                    XmlNamespaceManager xn = new XmlNamespaceManager(_pageDoc.NameTable);
                    xn.AddNamespace("x", "http://www.w3.org/1999/xhtml");

                    XmlElement historyNode = _pageDoc.SelectSingleNode("//x:li[@id='ca-history']", xn) as XmlElement;

                    if (null == historyNode)
                    {
                        _exists = false;
                    }
                    else
                    {
                        string historyLink = historyNode.InnerXml;
                        if (false == historyLink.Contains(Title))
                            _exists = false;
                    }
                     
                    */
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                }
            }
            else
            {
                _exists = true;

                string watchStatus = GetInputFieldValue("wpWatchthis");
                if (string.Empty == watchStatus)
                    _watch = null;
                else
                {
                    if (watchStatus == "1")
                        _watch = true;
                    else
                        _watch = false;
                }

                ParseCategories();
            }
		}

        private void ParseCategories()
        {
            string categoryFormat = string.Format(@"\[\[{0}:(?<cat>[^\]]+)\]\]", Language.LanguageProvider.Instance.Namespaces["Category"]);

            MatchCollection matches = Regex.Matches(_wikiMarkup, categoryFormat, RegexOptions.IgnoreCase | RegexOptions.Singleline);

            foreach (Match match in matches)
            {
                if (true == match.Groups["cat"].Success)
                    _categories.Add(match.Groups["cat"].Value);
            }            
        }

        /// <summary>
        /// Removes all [[Category:...]] links from the wiki markup, and replaces it with data from <see cref="Categories"/>.
        /// </summary>
        public void ReplaceCategories()
        {
            ClearCategories();

            foreach (string category in _categories)
                AddCategory(category);
        }

        /// <summary>
        /// Removes all [[Category:...]] links from the wiki markup.
        /// </summary>
        public void ClearCategories()
        {
            string categoryFormat = string.Format("\\n*\\[\\[{0}:[^\\]]+\\]\\]", Language.LanguageProvider.Instance.Namespaces["Category"]);

            _wikiMarkup = Regex.Replace(_wikiMarkup, categoryFormat, "", RegexOptions.IgnoreCase | RegexOptions.Singleline);
        }

        /// <summary>
        /// Adds a category to the wiki markup.
        /// </summary>
        /// <param name="category">The title of the category being added.</param>
        public void AddCategory(string category)
        {
            if (false == _categories.Contains(category))
                _categories.Add(category);

            _wikiMarkup += string.Format("\n[[{0}:{1}]]", Language.LanguageProvider.Instance.Namespaces["Category"], category);
        }
	}
}
